/* Water Tessellation Hull Shader */

#define CONTROL_POINTS	4

#include "TerrainUtils.hlsl"

// Matrix Buffer
cbuffer MatrixBuffer : register(b0)
{
	row_major float4x4 World			: WORLD;
	row_major float4x4 WorldViewProj	: WORLDVIEWPROJECTION;
	row_major float4x4 View				: VIEW;
};

// Tesselation Information
cbuffer TesselationInfo	: register(b1)
{
	float3	CameraPosition;
	float	MinimumDistance;
	float	MaximumDistance;
	float	MaximumTessFactor;
	float2	Padding;
};

// Hull Shader Input
struct HullInput
{
	float4 Position		: POSITION;
	float3 Normal		: NORMAL;
	float2 TexCoord		: TEXCOORD;
};

// Hull Shader Output
struct HullOutput
{
	float4 Position			: POSITION;
	float3 Normal			: NORMAL;
	float2 TexCoord			: TEXCOORD0;
};

// Hull Shader Constant Data Output
struct HullConstantDataOutput
{
	float	Edges[4]			: SV_TessFactor;
	float	Inside[2]			: SV_InsideTessFactor;
};

// Calculate Tesselation Factor
float CalculateTesselationFactor(float4 position)
{
	// Calculate the new Height
	float4 np = CalculateHeight(position);

	// Multiply with World Matrix
	float4 worldCentre = mul(np, World);

	// Calculate the Distance to this Patch
	float camDistance = distance(CameraPosition, worldCentre.xyz);

	// Calculate the Lerp
	float alpha = 1.0f - saturate((camDistance - MinimumDistance) / (MaximumDistance - MinimumDistance));

	// Lerp the TessFactor
	float tessFactor = lerp(1.0f, MaximumTessFactor, alpha);

	return tessFactor;
}

float CalculateEdgeTesselationFactor(HullInput a, HullInput b)
{
	float4 centre = lerp(a.Position, b.Position, 0.5f);

	return CalculateTesselationFactor(centre);
}

float CalculateFaceTesselationFactor(HullInput a, HullInput b, HullInput c)
{
	float4 edgeCentre = lerp(a.Position, c.Position, 0.5f);
	
	float4 centre = lerp(b.Position, edgeCentre, 0.5f);

	return CalculateTesselationFactor(centre);
}

// Hull Shader Constant Function
HullConstantDataOutput ConstantHSmain(InputPatch<HullInput, CONTROL_POINTS> inputPatch)
{
	HullConstantDataOutput output = (HullConstantDataOutput)0;

	output.Edges[0] = CalculateEdgeTesselationFactor(inputPatch[0], inputPatch[3]);
	output.Edges[1] = CalculateEdgeTesselationFactor(inputPatch[1], inputPatch[0]);
	output.Edges[2] = CalculateEdgeTesselationFactor(inputPatch[1], inputPatch[2]);
	output.Edges[3] = CalculateEdgeTesselationFactor(inputPatch[3], inputPatch[2]);

	output.Inside[0] = CalculateFaceTesselationFactor(inputPatch[0], inputPatch[1], inputPatch[2]);
	output.Inside[1] = CalculateFaceTesselationFactor(inputPatch[0], inputPatch[3], inputPatch[2]);

	return output;
}

// Hull Shader
[domain("quad")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(CONTROL_POINTS)]
[patchconstantfunc("ConstantHSmain")]
HullOutput main(InputPatch<HullInput, CONTROL_POINTS> input,
				uint idx : SV_OutputControlPointID)
{
	HullOutput output = (HullOutput)0;

	output.Position = input[idx].Position;
	output.Normal = input[idx].Normal;
	output.TexCoord = input[idx].TexCoord;

	return output;
}